package com.aloda.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.aloda.constant.ExceptionConstant;
import com.aloda.constant.PageConstant;
import com.aloda.constant.RedisConstant;
import com.aloda.constant.UserConstant;
import com.aloda.dto.*;
import com.aloda.entity.User;

import com.aloda.exception.BaseException;
import com.aloda.mapper.UserMapper;
import com.aloda.properties.SaltProperties;
import com.aloda.result.PageResult;
import com.aloda.service.UserService;
import com.aloda.tools.RedisOperateUtils;
import com.aloda.tools.UserHolder;
import com.aloda.utils.*;
import com.aloda.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author aloda
* @description 针对表【user(用户表)】的数据库操作Service实现
* @createDate 2024-10-21 08:47:29
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    @Resource
    private SaltProperties saltProperties;

    /**
     * 分页查询用户
     *
     * @param userPageDTO
     * @return
     */
    @Override
    public PageResult<UserPageVO> pageQuery(UserPageDTO userPageDTO) {
        // 开始分页查询
        Page<User> page = new Page<>(userPageDTO.getPage(),userPageDTO.getPageSize());
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(userPageDTO.getNickname() != null && !userPageDTO.getNickname().isEmpty(), User::getNickname, userPageDTO.getNickname())
                .like(userPageDTO.getPhone() != null && !userPageDTO.getPhone().isEmpty(), User::getPhone, userPageDTO.getPhone())
                .eq(userPageDTO.getUserStatus() != null, User::getUserStatus, userPageDTO.getUserStatus())
                .between(userPageDTO.getBegin() != null && userPageDTO.getEnd() != null, User::getCreateTime, userPageDTO.getBegin(), userPageDTO.getEnd())
                .orderByDesc(User::getUpdateTime);
        Page<User> userPage = userMapper.selectPage(page, lambdaQueryWrapper);
        // 对数据进行脱敏
        List<UserPageVO> userPageList = userPage.getRecords().stream()
                .map(user -> {
                    UserPageVO userPageVO = new UserPageVO();
                    BeanUtil.copyProperties(user, userPageVO);
                    return userPageVO;
                })
                .collect(Collectors.toList());
        return new PageResult<>(page.getTotal(), userPageList);
    }

    /**
     * 用户注册
     *
     * @param userRegisterDTO
     */
    @Override
    public void userRegister(UserRegisterDTO userRegisterDTO) {
        // 1.判断账号密码手机号验证码格式是否正确 1.1错误直接返回
        String userAccount = userRegisterDTO.getUserAccount();
        String userPassword = userRegisterDTO.getUserPassword();
        String phone = userRegisterDTO.getPhone();
        String code = userRegisterDTO.getCode();
        if (!AccountUtils.checkAccount(userAccount)) {
            throw new BaseException(ExceptionConstant.ACCOUNT_ERROR);
        }
        if (!PasswordUtils.checkPassword(userPassword)) {
            throw new BaseException(ExceptionConstant.PASSWORD_ERROR);
        }
        if (RegexUtils.isPhoneInvalid(phone)) {
            throw new BaseException(ExceptionConstant.PHONE_ERROR);
        }
        if (RegexUtils.isCodeInvalid(code)) {
            throw new BaseException(ExceptionConstant.VERIFY_ERROR);
        }
        // 2.根据手机号查询是否有对应的验证码缓存 2.1没有返回验证码错误
        String result = stringRedisTemplate.opsForValue().get(RedisConstant.PHONE_CODE_KEY + phone);
        if (result == null || result.isEmpty()){
            throw new BaseException(ExceptionConstant.PHONE_CODE_ERROR);
        }
        // 3.判断验证码是否正确 错误返回验证码错误
        if (!code.equals(result)) {
            throw new BaseException(ExceptionConstant.PHONE_CODE_ERROR);
        }
        // 验证码正确一次后直接失效
        stringRedisTemplate.delete(RedisConstant.PHONE_CODE_KEY + phone);
        // 4.判断该手机号/账号是否注册过 1.返回该手机号已注册 2.返回账号已注册过请换一个
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getPhone,phone);
        Long pResult = userMapper.selectCount(lambdaQueryWrapper);
        if (pResult > 0){
            throw new BaseException(ExceptionConstant.PHONE_REGISTERED);
        }
        LambdaQueryWrapper<User> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
        lambdaQueryWrapper1.eq(User::getUserAccount,userAccount);
        Long aResult = userMapper.selectCount(lambdaQueryWrapper1);
        if (aResult > 0) {
            throw  new BaseException(ExceptionConstant.ACCOUNT_NOT_UNIQUE);
        }
        // 5.密码加密
        String encryptUserPassword = DigestUtils
                .md5DigestAsHex((userPassword + saltProperties.getUserPasswordSalt()).getBytes());
        // 5.2获取一个随机昵称
        String nickname = RandomUtil.randomString(UserConstant.RANDOM_NICKNAME_LENGTH);
        // 6.新增一个用户-添加默认字段
        User user = User.builder()
                .nickname(nickname)
                .userAccount(userAccount)
                .userPassword(encryptUserPassword)
                .phone(phone)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .userStatus(UserConstant.DEFAULT_USER_STATES)
                .build();
        userMapper.insert(user);
    }

    /**
     * 用户登录
     *
     * @param userLoginDTO
     * @return
     */
    @Override
    public UserLoginVO userLogin(UserLoginDTO userLoginDTO) {
        // 1.校验用户账号和密码格式
        String userAccount = userLoginDTO.getUserAccount();
        String userPassword = userLoginDTO.getUserPassword();
        if (!AccountUtils.checkAccount(userAccount)) {
            throw new BaseException(ExceptionConstant.ACCOUNT_ERROR);
        }
        if (!PasswordUtils.checkPassword(userPassword)) {
            throw new BaseException(ExceptionConstant.PASSWORD_ERROR);
        }
        // 2.校验验证码
        String code = userLoginDTO.getCode();
        VerifyServiceImpl.checkVerify(code,userLoginDTO.getKey(),stringRedisTemplate);
        // 3.查询账号密码是否正确 select * from user where user_account = ? and user_password = ?
        // 将密码转换为加密状态
        String transPassword = DigestUtils.md5DigestAsHex((userPassword + saltProperties.getUserPasswordSalt()).getBytes());
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getUserAccount,userAccount)
                .eq(User::getUserPassword,transPassword);
        User user = userMapper.selectOne(lambdaQueryWrapper);
        if (user == null) {
            // 未查到说明用户名或密码错误
            throw new BaseException(ExceptionConstant.LOGIN_FAIL);
        }
        // 4.校验账号状态
        if (user.getUserStatus() == UserConstant.EX_USER_STATUS) {
            throw new BaseException(ExceptionConstant.ACCOUNT_EX_CODE,ExceptionConstant.ACCOUNT_EX);
        }
        // 5.生成token
        String token = UUID.randomUUID().toString();
        // 6.将token作为key将用户信息保存在redis
        RedisOperateUtils.saveUserInRedis(user,token,stringRedisTemplate);
        // 7.返回vo
        return UserLoginVO.builder()
                .id(user.getId())
                .userAccount(user.getUserAccount())
                .token(token)
                .build();
    }

    /**
     * 获取用户主页信息
     *
     * @return
     */
    @Override
    public UserInfoVO getUserPageInfo() {
        UserSafetyDTO userSafetyDTO = UserHolder.getUser();
        User user = userMapper.selectById(userSafetyDTO.getId());
        // TODO: 后期需要获取粉丝数量，关注数量，队伍数量等
        return UserInfoVO.builder()
                .nickname(user.getNickname())
                .avatar(user.getAvatar())
                .build();
    }

    /**
     * 获取用户设置页信息
     *
     * @return
     */
    @Override
    public UserSetVO getUserSetInfo() {
        UserSafetyDTO userSafetyDTO = UserHolder.getUser();
        User user = userMapper.selectById(userSafetyDTO.getId());
        return UserSetVO.builder()
                .nickname(user.getNickname())
                .email(user.getEmail())
                .gender(user.getGender())
                .avatar(user.getAvatar())
                .description(user.getDescription())
                .build();
    }

    /**
     * 用户退出
     *
     * @param token
     */
    @Override
    public void userLogout(String token) {
        // 删除用户的登录状态
        Boolean result = RedisOperateUtils.deleteUserInRedis(token, stringRedisTemplate);
        if (!result) {
            throw new BaseException();
        }
    }

    /**
     * 用户修改个人基本信息
     *
     * @param userSetDTO
     */
    @Override
    public void updateUserInfo(UserSetDTO userSetDTO) {
        String nickname = userSetDTO.getNickname();
        if (!NicknameUtils.checkNickname(nickname)) {
            throw new BaseException(ExceptionConstant.NICKNAME_ERROR);
        }
        String email = userSetDTO.getEmail();
        if (!EmailUtils.checkEmail(email)) {
            throw new BaseException(ExceptionConstant.EMAIL_ERROR);
        }
        String description = userSetDTO.getDescription();
        if (!DescriptionUtils.checkDescription(description)) {
            throw new BaseException(ExceptionConstant.DESC_ERROR);
        }
        Integer gender = userSetDTO.getGender();
        if (gender != 1 && gender != 0){
            throw new BaseException(ExceptionConstant.GENDER_ERROR);
        }
        User user = userMapper.selectById(UserHolder.getUser().getId());
        BeanUtil.copyProperties(userSetDTO,user);
        user.setUpdateTime(LocalDateTime.now()); // 刷新更新时间 TODO: 后期考虑操作日志
        userMapper.updateById(user);
    }

    /**
     * 获取用户所有标签
     *
     * @return
     */
    @Override
    public List<TagSimpleVO> getUserTagList() {
        // 获取当前用户id
        Long id = UserHolder.getUser().getId();
        // 获取该用户所有标签
        String tagsJson = userMapper.getUserTags(id);
        // 将json字符串反序列化为list
        Gson gson = new Gson();
        List<String> tags = gson.fromJson(tagsJson, List.class);
        if (tags == null || tags.isEmpty()) {
            return null;
        }
        // 返回前端所需的数据格式
        return tags.stream().map(tag -> {
            TagSimpleVO tagSimpleVO = new TagSimpleVO();
            tagSimpleVO.setId((long) tags.indexOf(tag));
            tagSimpleVO.setText(tag);
            return tagSimpleVO;
        }).toList();
    }

    /**
     * 用户修改自己的标签
     *
     * @param tags
     */
    @Override
    public void updateUserTags(List<String> tags) {
        Gson gson = new Gson();
        String tagsToJson = gson.toJson(tags);
        Long id = UserHolder.getUser().getId();
        User user = userMapper.selectById(id);
        user.setTags(tagsToJson);
        userMapper.updateById(user);
    }

    /**
     * 根据标签搜索用户
     * @param userPageByTagDTO 分页查询dto
     * @return
     */
    public List<UserSearchVO> searchUsersByTags(UserPageByTagDTO userPageByTagDTO) {
        List<String> tagNameList = userPageByTagDTO.getTags();
        // 1.标签为空返回异常
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BaseException(ExceptionConstant.TAGS_LIST_EMPTY);
        }
        // 2.构建查询语句 select * from user where tags like '%java%' and like 'c++'
        // 2.1 第一代查询方案
        Page<User> userPage = new Page<>(userPageByTagDTO.getCurrentPage(), PageConstant.DEFAULT_PAGE_SIZE);
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        tagNameList.forEach( tagName -> lambdaQueryWrapper.like(User::getTags,tagName));
        Page<User> page = userMapper.selectPage(userPage, lambdaQueryWrapper);
        List<User> userList = page.getRecords();
//        // 2.2 第二代方案 先查询所有用户，在内存中计算是否拥有要求的标签
//        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//        List<User> userList = userMapper.selectList(lambdaQueryWrapper);
//        Gson gson = new Gson();
//        return userList.stream().filter(user -> {
//            String tagsStr = user.getTags();
//            if (tagsStr == null || tagsStr.isEmpty()) {
//                return false;
//            }
//            Set<String> tempTagNameSet = gson.fromJson(tagsStr, new TypeToken<Set<String>>() {
//            }.getType());
//            for (String tagName : tagNameList) {
//                if (!tempTagNameSet.contains(tagName)) {
//                    return false;
//                }
//            }
//            return true;
//        }).map(user -> {
//            UserSafetyVO userSafetyVO = new UserSafetyVO();
//            BeanUtil.copyProperties(user, userSafetyVO);
//            return userSafetyVO;
//        }).toList();
        // 3.数据脱敏
        return userList.stream().map(user -> {
            UserSearchVO userSearchVO = UserSearchVO.builder()
                    .id(user.getId())
                    .avatar(user.getAvatar())
                    .nickname(user.getNickname())
                    .description(user.getDescription())
                    .build();
            Gson gson = new Gson();
            List tagList = gson.fromJson(user.getTags(), List.class);
            userSearchVO.setTags(tagList);
            return userSearchVO;
        }).toList();
    }
}




